/*
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is ST_EXT-S_UIMAST.
 *
 * The Initial Developer of the Original Code is
 * University of Rome, Tor Vergata
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 */

package it.uniroma2.art.uimast.impl;

import it.uniroma2.art.ontapi.ARTRepository;
import it.uniroma2.art.ontapi.ARTResource;
import it.uniroma2.art.semanticturkey.resources.Resources;
import it.uniroma2.art.uimast.xpts.ExportModeDriver;
import it.uniroma2.art.uimast.xpts.Variant;

import java.util.Iterator;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.w3c.dom.Element;


/**
 * A data source. It provides the values which an instance of {@link FeatureProjection} assignes to
 * a feature.
 * @author <a href="mailto:manuel.fiorelli@gmail.com">Manuel Fiorelli</a>
 */

public abstract class DataSource extends CommonDOMFacilities
{
	/**
	 * Creates a <code>DataSource</code> object.
	 * @param v the prototype variant (of the appropriate concrete class)
	 */
    public DataSource(Variant v)
    {
        this.v = v;
    }

    /**
     * Gets an iterator over the data.
     * @param domainObject the current domain object
     * @param annot the current annotation
     * @return an iterator
     */
    public abstract Iterator<Variant> apply(ARTResource domainObject, ARTResource annot);

    /**
     * A factory method.
     * @param classProjection the source class projection
     * @param typeSystem the type system
     * @param elementType the element type
     * @param e the DOM element describing the data source
     * @return a data source
     * @throws Exception
     */
    public static DataSource create(ClassProjection classProjection, TypeSystem typeSystem, Type elementType, Element e)
        throws Exception
    {
        if(hasAttributeNS(e, AnnotationProjection.PROJ_NS, "value"))
            return create(classProjection, typeSystem, elementType, "value", getAttributeNS(e, AnnotationProjection.PROJ_NS, "value"));
        if(hasAttributeNS(e, AnnotationProjection.PROJ_NS, "property"))
            return create(classProjection, typeSystem, elementType, "property", getAttributeNS(e, AnnotationProjection.PROJ_NS, "property"));
        else
            throw new Exception("Missing \"value\" or \"property\" attribute");
    }

    /**
     * A factory method.
     * @param classProjection the source class projection
     * @param typeSystem the type system
     * @param elementType the element type
     * @param mode the projection mode: either <code>value</code> or <code>property</code>
     * @param v the value lexical representation or the property URI
     * @return a data source
     * @throws Exception
     */
    public static DataSource create(ClassProjection classProjection, TypeSystem typeSystem, Type elementType, String mode, String value)
        throws Exception
    {
        if(mode.equals("value"))
            return new ValueDataSource(Variant.create(typeSystem, elementType), value);
        
        if(mode.equals("property"))
        {
            ARTRepository repository = Resources.getRepository();
            ARTResource property = repository.createURIResource(value);
            
            Variant v = Variant.create(typeSystem, elementType);
            
            ExportModeDriver provider = classProjection.getExportModeProvider();
            
            ExportModeDriver.CheckResult r = provider.checkWiredProperty(property, v);
            
            if (r == ExportModeDriver.CheckResult.UNSUPPORTED_WIRED_PROPERTY) {
             	return new PropertyDataSource(Variant.create(typeSystem, elementType), classProjection.getSourceClass(), property);
            } else if (r == ExportModeDriver.CheckResult.OK) {
            	return new WiredPropertySource(v, property, classProjection);
            } else {
            	throw new Exception("The wired property \"" + value +"\" has a range incompatible with the feature range");
            }
        } else {
            throw new Exception("Cannot understand the projection mode \"" + mode + "\"");
        }
    }

    /**
     * Serializes this data source.
     * @param e A DOM element to augment
     */
    public abstract void toDOM(Element element);

    /** The prototype variant */
    protected Variant v;
}
